package org.shopen.puremvc.view.components.drawingcanvas
{
	import common.BitmapHelper;
	
	import flash.display.BitmapData;
	import flash.display.DisplayObject;
	import flash.display.IBitmapDrawable;
	import flash.filters.BitmapFilter;
	import flash.geom.Matrix;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	
	import graph.drawingshapes.BaseFill;
	import graph.drawingshapes.DrawingShapeBase;
	import graph.drawingshapes.GradientFill;
	import graph.drawingshapes.NoFill;
	import graph.drawingshapes.RedrawEvent;
	import graph.drawingshapes.shapes.DrawingShape;
	
	import mx.core.UIComponent;
	import mx.events.PropertyChangeEvent;

	/**
	 * Singleton 
	 * @author Андрій
	 * 
	 */	
	public class DrawingCanvas extends UIComponent
	{
		public static const DefaultX:Number = 0;
		public static const DefaultY:Number = 0;
		
		private static var _instance:DrawingCanvas = null;
		public static function get instance():DrawingCanvas
		{
			if (_instance == null)
			{
				_instance = new DrawingCanvas(new PrivateClass()); 
			}
			return _instance;
		}
		
		[Bindable]
		private var _fill:BaseFill= new NoFill();
		[Bindable]
		public function get fill():BaseFill
		{
			return this._fill;
		}
		public function set fill(value:BaseFill):void
		{
			firePropertyChanged("fill", this.fill, value);
			this._fill = value;
			this._fill.addEventListener(RedrawEvent.REDRAW, onRedrawEvent);
			this._fill.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, onProperyChangeEvent);
			dispatchEvent(new RedrawEvent());
		}
		
		public static const DefaultWidth:Number = 600;
		[Bindable]
		public override function get width():Number
		{
			return super.width;
		}
		public override function set width(value:Number):void
		{
			if (value < 0)
			{
				value = 0;
			}
			if (this.width != value)
			{
				firePropertyChanged("width", this.width, value);
				super.width = value;
				dispatchEvent(new RedrawEvent());
			}
		}
		
		public static const DefaultHeight:Number = 400;
		[Bindable]
		public override function get height():Number
		{
			return super.height;
		}
		public override function set height(value:Number):void
		{
			if (value < 0)
			{
				value = 0;
			}
			if (this.height != value)
			{
				firePropertyChanged("height", this.height, value);
				super.height = value;
				dispatchEvent(new RedrawEvent());
			}
		}
		
		[Bindable]
		protected var _bitmapData:BitmapData = new BitmapData(DefaultWidth, DefaultHeight, true, DefaultBackgroundColor);
		[Bindable]
		public function get bitmapData():BitmapData
		{
			return this._bitmapData;
		}
		public function set bitmapData(value:BitmapData):void
		{
			if (this.bitmapData != value)
			{
				var event:PropertyChangeEvent = PropertyChangeEvent.createUpdateEvent(this, "bitmapData", this.bitmapData, value);
				dispatchEvent(event);
				this._bitmapData = value;
				dispatchEvent(new RedrawEvent());
			}
		}
		
		public static const DefaultZoomFactor:Number = 100;
		[Bindable]
		protected var _zoomFactor:Number = 100;
		[Bindable]
		public function get zoomFactor():Number
		{
			return this._zoomFactor;
		}
		public function set zoomFactor(value:Number):void
		{
			if (this.zoomFactor != value)
			{
				var event:PropertyChangeEvent = PropertyChangeEvent.createUpdateEvent(this, "zoomFactor", this.zoomFactor, value);
				dispatchEvent(event);
				this._zoomFactor = value;
				dispatchEvent(new RedrawEvent());
			}
		}
		
		protected function get zoomMatrix():Matrix
		{
			var matr:Matrix = new Matrix();
			matr.scale(this.zoomFactor/100, this.zoomFactor/100);
			return matr;
		}
		
		public static const DefaultBackgroundColor:uint = 0xFFFFFF;
		
		
		public function DrawingCanvas(privateClass:PrivateClass, x:Number = DefaultX, y:Number = DefaultY, width:Number = DefaultWidth, height:Number = DefaultHeight)
		{
			super();
			initSize(x, y, width, height);
			dispatchEvent(new RedrawEvent());
		}
		
		public function init(x:Number = DefaultX, y:Number = DefaultY, width:Number = DefaultWidth, height:Number = DefaultHeight):void
		{
			this.cacheAsBitmap = true;
			this.
			initSize(x, y, width, height);
			initStyle();
			clear();
			initHandlers();
			dispatchEvent(new RedrawEvent());
		}
		
		public function initSize(x:Number = DefaultX, y:Number = DefaultY, width:Number = DefaultWidth, height:Number = DefaultHeight):void
		{
			this.x = x;
			this.y = y;
			this.width = width;
			this.height = height;
		}
			
		protected function initStyle():void
		{
			this.fill = new GradientFill();
		}
		
		protected function initHandlers():void
		{
			this.addEventListener(RedrawEvent.REDRAW, onRedrawEvent);
			this.fill.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, onProperyChangeEvent);
			this.fill.addEventListener(RedrawEvent.REDRAW, onRedrawEvent);
		}
		
		public function mergeAll():void
		{
			for(var i : int = 0; i < this.numChildren; ++i)
			{
				var obj:DisplayObject = this.getChildAt(i);
				if (!(obj is DrawingShape)) continue;
				
				var shape:DrawingShape = (obj as DrawingShape);
								
				mergeShape(shape);
			}
		}
		
		public function mergeShape(shape:DrawingShapeBase):void
		{
			merge(shape, shape.x, shape.y);
			dispatchEvent(new RedrawEvent());
		}
		
		public function merge(shape:IBitmapDrawable, x:Number, y:Number):void
		{
			var translateMatrix : Matrix = BitmapHelper.getTranslateMatrix(x, y);
			var oldBitmapData:BitmapData = this.bitmapData.clone();
			this.bitmapData.draw(shape, translateMatrix);
			
			
			firePropertyChanged("bitmapData", oldBitmapData, this.bitmapData);
			dispatchEvent(new RedrawEvent());
		}
		
		public function draw(shape:IBitmapDrawable, x:Number, y:Number):void
		{
			var translateMatrix : Matrix = BitmapHelper.getTranslateMatrix(x, y);
			var oldBitmapData:BitmapData = this.bitmapData.clone();
			bitmapData.draw(shape, translateMatrix);
			
			firePropertyChanged("bitmapData", oldBitmapData, this.bitmapData);			
			dispatchEvent(new RedrawEvent());
		}
		
		public function removeAll():void
		{
			while (this.numChildren > 0)
			{
				this.removeChildAt(0);
			}
		}
		
		public function clear():void
		{
			this.graphics.clear();
			
			this.bitmapData = new BitmapData(this.width, this.height, false, DefaultBackgroundColor);
			drawCanvas(this.width, this.height);
			
			dispatchEvent(new RedrawEvent());
		}
		
		protected function drawCanvas(width:Number, height:Number):void
		{
			this.graphics.clear();
			this.fill.beginFill(this.graphics);
			this.graphics.drawRect(0, 0, width, height);
			this.fill.endFill(this.graphics);
		}
		
		protected virtual function onProperyChangeEvent(event:PropertyChangeEvent):void
		{
			dispatchEvent(event);
		}
		
		protected virtual function onRedrawEvent(event:RedrawEvent):void
		{
			this.invalidateDisplayList();
		}
		
		protected override function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
		{
			super.updateDisplayList(unscaledWidth, unscaledHeight);
			this.drawCanvas(unscaledWidth, unscaledHeight);
			this.graphics.beginBitmapFill(this.bitmapData, null, false, true);
			this.graphics.drawRect(0,0,unscaledWidth, unscaledHeight);
			this.graphics.endFill();
			this.scaleX = zoomFactor/100;
			this.scaleY = zoomFactor/100;
		}
	
		public function applyFilter(filter:BitmapFilter, rect:Rectangle = null):void
		{
			if (rect == null)
				rect = this.getRect(this);
			var oldBitmapData:BitmapData = this.bitmapData.clone();
			this.bitmapData.applyFilter(this.bitmapData, rect, new Point(rect.x, rect.y), filter);
			
			firePropertyChanged("bitmapData", oldBitmapData, this.bitmapData);
			dispatchEvent(new RedrawEvent());
		}
		
		protected function firePropertyChanged(property:String, oldValue:Object, newValue:Object):void
		{
			if (!this.hasOwnProperty(property)) return;
			var event:PropertyChangeEvent = PropertyChangeEvent.createUpdateEvent(this, property, oldValue, newValue);
			dispatchEvent(event);			
		}
	}
}

class PrivateClass
{
	public function PrivateClass()
	{
		
	}
}